{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 位置参数\n",
    "- 默认参数\n",
    "- 可变参数\n",
    "- 关键字参数\n",
    "- 命名关键字参数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 位置参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25\n",
      "125\n"
     ]
    }
   ],
   "source": [
    "\n",
    "def power(x):\n",
    "    return x * x\n",
    "print(power(5)) \n",
    "def power(x, n):# 调用函数时传入的两个值按照位置顺序依次赋给参数x和n\n",
    "\n",
    "    s = 1\n",
    "    while n > 0:\n",
    "        n = n - 1\n",
    "        s = s * x\n",
    "    return s\n",
    "print(power(5, 3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 默认参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "power() missing 1 required positional argument: 'n'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-5-6ad766abe6a2>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 默认参数\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpower\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m5\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: power() missing 1 required positional argument: 'n'"
     ]
    }
   ],
   "source": [
    "print(power(5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25\n"
     ]
    }
   ],
   "source": [
    "def power(x, n=2): # 把第二个参数n的默认值设定为2：\n",
    "    s = 1\n",
    "    while n > 0:\n",
    "        n = n - 1\n",
    "        s = s * x\n",
    "    return s\n",
    "print(power(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从上面的例子可以看出，默认参数可以简化函数的调用。设置默认参数时，有几点要注意：\n",
    "\n",
    "一是必选参数在前，默认参数在后，否则Python的解释器会报错（思考一下为什么默认参数不能放在必选参数前面）；\n",
    "\n",
    "二是如何设置默认参数。\n",
    "\n",
    "当函数有多个参数时，把变化大的参数放前面，变化小的参数放后面。变化小的参数就可以作为默认参数。\n",
    "\n",
    "使用默认参数有什么好处？最大的好处是能降低调用函数的难度。\n",
    "\n",
    "*** 定义默认参数要牢记一点：默认参数必须指向不变对象！"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 可变参数\n",
    "-  传入的参数个数是可变的，可以是1个、2个到任意个，还可以是0个。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 案例:\n",
    "# 定一组数字a，b，c……，请计算a**2 + b**2 + c**2 + ……\n",
    "def calc(numbers):\n",
    "    sum = 0\n",
    "    for n in numbers:\n",
    "        sum = sum + n * n\n",
    "    return sum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "14\n"
     ]
    }
   ],
   "source": [
    "# 调用的时候，需要先组装出一个list或tuple：\n",
    "print(calc((1, 2, 3)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "14\n"
     ]
    }
   ],
   "source": [
    "# 如果利用可变参数，调用函数的方式可以简化成这样:\n",
    "def calc(*numbers): # 参数前面加了一个*号\n",
    "    sum = 0\n",
    "    for n in numbers:\n",
    "        sum = sum + n * n\n",
    "    return sum\n",
    "print(calc(1, 2, 3 ))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 关键字参数\n",
    "可变参数允许你传入0个或任意个参数，这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数，这些关键字参数在函数内部自动组装为一个dict。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Michael age: 30 other: {}\n"
     ]
    }
   ],
   "source": [
    "# 案例:\n",
    "def person(name, age, **kw):\n",
    "    print('name:', name, 'age:', age, 'other:', kw)\n",
    "person('Michael', 30)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数person除了必选参数name和age外，还接受关键字参数kw。在调用该函数时，可以只传入必选参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Bob age: 35 other: {'city': 'Beijing'}\n",
      "name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}\n"
     ]
    }
   ],
   "source": [
    "person('Bob', 35, city='Beijing')\n",
    "person('Adam', 45, gender='M', job='Engineer')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}\n",
      "name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}\n"
     ]
    }
   ],
   "source": [
    "# 可以先组装出一个dict，然后，把该dict转换为关键字参数传进去\n",
    "extra = {'city': 'Beijing', 'job': 'Engineer'}\n",
    "person('Jack', 24, city=extra['city'], job=extra['job'])\n",
    "# 上面复杂的调用可以用简化的写法\n",
    "extra = {'city': 'Beijing', 'job': 'Engineer'}\n",
    "person('Jack', 24, **extra)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 命名关键字参数\n",
    "和关键字参数**kw不同，命名关键字参数需要一个特殊分隔符*，*后面的参数被视为命名关键字参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 例如，只接收city和job作为关键字参数。这种方式定义的函数如下：\n",
    "def person(name, age, *, city, job):\n",
    "    print(name, age, city, job)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jack 24 Beijing Engineer\n"
     ]
    }
   ],
   "source": [
    "# 调用:\n",
    "\n",
    "person('Jack', 24, city='Beijing', job='Engineer')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 如果函数定义中已经有了一个可变参数，后面跟着的命名关键字参数就不再需要一个特殊分隔符*了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def person(name, age, *args, city, job):\n",
    "    print(name, age, args, city, job)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 命名关键字参数必须传入参数名，这和位置参数不同。如果没有传入参数名，调用将报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "person() missing 2 required keyword-only arguments: 'city' and 'job'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-20-e7e3c3c0aaca>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mperson\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Jack'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m24\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'Beijing'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'Engineer'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: person() missing 2 required keyword-only arguments: 'city' and 'job'"
     ]
    }
   ],
   "source": [
    "person('Jack', 24, 'Beijing', 'Engineer')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 命名关键字参数可以有缺省值，从而简化调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jack 24 Beijing Engineer\n"
     ]
    }
   ],
   "source": [
    "def person(name, age, *, city='Beijing', job):\n",
    "    print(name, age, city, job)\n",
    "person('Jack', 24, job='Engineer')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 参数组合\n",
    "在Python中定义函数，可以用位置参数、默认参数、可变参数、关键字参数和命名关键字参数，这5种参数都可以组合使用。但是请注意，参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f1(a, b, c=0, *args, **kw):\n",
    "    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)\n",
    "\n",
    "def f2(a, b, c=0, *, d, **kw):\n",
    "    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在函数调用的时候，Python解释器自动按照参数位置和参数名把对应的参数传进去。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 0 args = () kw = {}\n",
      "a = 1 b = 2 c = 3 args = () kw = {}\n",
      "a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}\n",
      "a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}\n",
      "a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}\n"
     ]
    }
   ],
   "source": [
    "f1(1, 2)\n",
    "f1(1, 2, c=3)\n",
    "f1(1, 2, 3, 'a', 'b')\n",
    "f1(1, 2, 3, 'a', 'b', x = 99)\n",
    "f2(1, 2, d = 99, ext = None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于任意函数，都可以通过类似func(*args, **kw)的形式调用它，无论它的参数是如何定义的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}\n",
      "a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}\n"
     ]
    }
   ],
   "source": [
    "args = (1, 2, 3, 4)\n",
    "kw = {'d': 99, 'x': '#'}\n",
    "f1(*args, **kw)\n",
    "args = (1, 2, 3)\n",
    "kw = {'d': 88, 'x': '#'}\n",
    "f2(*args, **kw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 练习\n",
    "以下函数允许计算两个数的乘积，请稍加改造，变成可接收一个或多个数并计算乘积：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "def product(x, y):\n",
    "    return x * y\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "12"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 需要传入一个或多个参数,用可变参数:\n",
    "def product(*num):\n",
    "    p = 1\n",
    "    for i in num:\n",
    "        p *= i\n",
    "    return p\n",
    "num = [1,4,3]\n",
    "product(*num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 参数为空list或tulpe时\n",
    "def product(*num):\n",
    "    if len(num) == 0:\n",
    "        raise TypeError\n",
    "    p = 1\n",
    "    for i in num:\n",
    "        p *= i\n",
    "    return p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-58-9f4dab78d931>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mnum\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mproduct\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mnum\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-57-e3805a0e8e78>\u001b[0m in \u001b[0;36mproduct\u001b[1;34m(*num)\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mproduct\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mnum\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      5\u001b[0m     \u001b[0mp\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      6\u001b[0m     \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mnum\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: "
     ]
    }
   ],
   "source": [
    "num = ()\n",
    "product(*num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
